package com.softwaremill.common.faces.transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
/**
* 1 transaction for GET: (before RESTORE_VIEW, after RENDER_RESPONSE)
* 2 transactions for POST: (before RESTORE_VIEW, after INVOKE_APPLICATION), (before RENDER_RESPONSE, after RENDER_RESPONSE)
* @author Adam Warski (adam at warski dot org)
*/
public class TransactionPhaseListener implements PhaseListener {
private static final Logger log = LoggerFactory.getLogger(TransactionPhaseListener.class);
public void beforePhase(PhaseEvent event) {
// Always starting before RESTORE_VIEW
if (PhaseId.RESTORE_VIEW.equals(event.getPhaseId())) {
log.debug("Start transaction before RESTORE_VIEW");
startTransaction(event.getFacesContext());
}
// Starting before RENDER_RESPONSE in case this is a postback
if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()) && event.getFacesContext().isPostback()) {
log.debug("Start transaction before RENDER_RESPONSE, during a postback");
startTransaction(event.getFacesContext());
}
}
public void afterPhase(PhaseEvent event) {
// Commiting if the response is complete
if (event.getFacesContext().getResponseComplete()) {
log.debug("Commit transaction as response is complete");
commitTransaction(event.getFacesContext());
}
// Always commiting after RENDER_RESPONSE
if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId())) {
log.debug("Commit transaction after RENDER_RESPONSE");
commitTransaction(event.getFacesContext());
}
// Commiting after INVOKE_APPLICATION in case of a postback
if (PhaseId.INVOKE_APPLICATION.equals(event.getPhaseId()) && event.getFacesContext().isPostback()) {
log.debug("Commit transaction after INVOKE_APPLICATION, during a postback");
commitTransaction(event.getFacesContext());
}
}
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
private UserTransaction getUserTransaction() throws NamingException {
return (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");
}
private static final String STARTED_TX_KEY = "_started_tx_";
private void startTransaction(FacesContext facesContext) {
try {
UserTransaction utx = getUserTransaction();
if (utx.getStatus() != Status.STATUS_ACTIVE) {
utx.begin();
facesContext.getAttributes().put(STARTED_TX_KEY, new Object());
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new FacesException(e);
}
}
private void commitTransaction(FacesContext facesContext) {
try {
UserTransaction utx = getUserTransaction();
if (facesContext.getAttributes().containsKey(STARTED_TX_KEY)) {
if (utx.getStatus() == Status.STATUS_ACTIVE) {
utx.commit();
} else {
if (utx.getStatus() != Status.STATUS_ROLLEDBACK) {
utx.rollback();
}
}
facesContext.getAttributes().remove(STARTED_TX_KEY);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new FacesException(e);
}
}
}